package com.youlin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.youlin.dto.*;
import com.youlin.dto.req.SignInReqDto;
import com.youlin.dto.res.SIContentResDto;
import com.youlin.dto.res.SINameListResDto;
import com.youlin.entities.*;
import com.youlin.mapper.BygSignInMapper;
import com.youlin.service.BygAppointmentService;
import com.youlin.service.BygSignInHistoryService;
import com.youlin.service.BygSignInService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlin.service.BygWordService;
import com.youlin.untils.*;
import org.apache.commons.lang.StringUtils;
import org.assertj.core.util.Lists;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.lang.ref.SoftReference;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Service
public class BygSignInServiceImpl extends ServiceImpl<BygSignInMapper, BygSignIn> implements BygSignInService {

    private SoftReference<String> wordSr = new SoftReference<>(null);
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private BygSignInHistoryService bygSignInHistoryService;
    @Autowired
    private BygAppointmentService bygAppointmentService;
    @Autowired
    private BygWordService bygWordService;
    @Autowired
    private ConcurrentHashMap<String, SoftReference<Integer>> amStatusCMap;
    @Autowired
    private MyCacheUtil myCacheUtil;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 来宾签到
     */
    @Override
    public BaseResDto<Status, String> saveSignIn(SignInReqDto data) {
        // 获取当前签到告别会的缓存数据，验证是否可以继续签到
        try {
            Integer status = myCacheUtil.getFromCache(amStatusCMap, MyEnum.AMUUID_STATUS,
                    data.getAmUuId(), MyEnum.AMBF, Integer.class, MyEnum.SIGNINLOCK, () -> {
                        BygAppointment appointment = bygAppointmentService.findAppointmentByUuId(data.getAmUuId());
                        if (appointment == null) throw new ParamErrorExpectation();
                        return appointment.getStatus();
                    });
            if (status >= MyEnum.AM_REVIEWING.getValue()) return new BaseResDto<>(Status.AMDONE);
        } catch (ParamErrorExpectation e) {
            e.printStackTrace();
            return new BaseDataResDto<>(Status.PARAMETERERROR);
        } catch (Exception e) {
            e.printStackTrace();
            return new BaseDataResDto<>(Status.ERROR);
        }

        // 保存签到数据到数据库
        BygSignIn bygSignIn = new BygSignIn().setUuId(data.getSiId())
                .setAmUuId(data.getAmUuId()).setSitId(data.getSitId())
                .setName(data.getName()).setContent(data.getContent())
                .setInTime(LocalDateTime.now());
        if (! saveOrUpdate(bygSignIn)) return new BaseResDto<>(Status.ERROR);

        // 提交签到任务到线程池异步执行
        threadPoolExecutor.execute(() -> {
            String name = data.getName();
            String content = data.getContent();
            if (StringUtils.isNotBlank(name) && name.equals("逝者好友") &&
                    StringUtils.isNotBlank(content) && content.equals("逝者已矣，生者如斯")) return;

            // 获取验证签名和留言的敏感词缓存数据
            if (StringUtils.isBlank(wordSr.get())) { // 首先从本地缓存获取
                RedisUtil.RedisSupplier<String> wordRS = RedisUtil.getObjectLocked(redisTemplate,
                        "words", String.class, redissonClient, "sensitiveWords", () -> {
                            String words = bygWordService.list().parallelStream()
                                    .map(BygWord::getWord).collect(Collectors.joining(";"));
                            return new RedisUtil.RedisSupplier<>(words, Status.SUCCESS);
                        });
                //if (! wordRS.isSuccess()) return new BaseResDto<>(wordRS.getStatus());
                wordSr = new SoftReference<>(wordRS.getRedisValue());
            }

            // 验证签名和留言的敏感词
            StringBuilder nameHit = new StringBuilder();
            StringBuilder contentHit = new StringBuilder();
            Arrays.asList(wordSr.get().split(";")).parallelStream().forEach(w -> {
                if (StringUtils.isNotBlank(name) && !name.equals("逝者好友")
                        && name.contains(w)) nameHit.append(w + ";");
                if (StringUtils.isNotBlank(content) && !content.equals("逝者已矣，生者如斯")
                        && content.contains(w)) contentHit.append(w + ";");
            });
            if (StringUtils.isNotBlank(nameHit.toString()) || StringUtils.isNotBlank(nameHit.toString()))
                saveOrUpdate(bygSignIn.setNameHit(nameHit.toString()).setContentHit(contentHit.toString()));
        });

        return new BaseResDto<>(Status.SUCCESS);
    }

    /**
     * 获取待审核来宾签名列表
     * @param amId
     * @return
     */
    @Override
    public BaseDataResDto<List<SINameListResDto>> findAllSignInNames(Integer amId) {
        BygAppointment appointment = bygAppointmentService.getById(amId);
        // 获取当前告别会签到数据集合
        List<BygSignIn> signIns = list(Wrappers.lambdaQuery(BygSignIn.class)
                .eq(BygSignIn::getAmUuId, appointment.getUuId()).isNotNull(BygSignIn::getName));
        List<SINameListResDto> listList = Lists.newArrayList();
        // 按签名姓氏拼音首字母排序分组
        if (MyBeanUtil.isNotEmpty(signIns)) {
            listList = PinYinSortUtil.pinYinSort(signIns);
            SINameListResDto dto = listList.get(0);
            if (dto.getAbc().equals("#")) {
                listList.add(listList.get(0));
                listList.remove(0);
            }
        }
        return new BaseDataResDto<List<SINameListResDto>>(Status.SUCCESS).setData(listList);
    }

    /**
     * 获取待审核来宾祝福列表
     * @param amId
     * @return
     */
    @Override
    public BaseDataResDto<List<SIContentResDto>> findAllSignInContents(Integer amId) {
        BygAppointment appointment = bygAppointmentService.getById(amId);
        // 获取当前告别会留言数据集合
        LambdaQueryWrapper<BygSignIn> wrapper = Wrappers.lambdaQuery(BygSignIn.class)
                .eq(BygSignIn::getAmUuId, appointment.getUuId())
                .isNotNull(BygSignIn::getContent).orderByDesc(BygSignIn::getInTime);
        List<SIContentResDto>siContentResDtos  = list(wrapper).parallelStream()
                .map(si -> BaseService.copyProperties(si, SIContentResDto.class)).collect(Collectors.toList());
        return new BaseDataResDto<List<SIContentResDto>>(Status.SUCCESS).setData(siContentResDtos);
    }

    /**
     * 审核来宾签到：来宾签名/来宾留言
     * @param siUuIds
     * @param type
     * @return
     */
    @Override
    @Transactional
    public BaseResDto<Status, String> reviewSignIn(List<String> siUuIds, Integer type) {
        LambdaUpdateWrapper<BygSignIn> updateWrapper = Wrappers.lambdaUpdate(BygSignIn.class);
        // 审核来宾签名
        if (MyEnum.SI_REVIEW_NAME.getValue() == type)
            updateWrapper.set(BygSignIn::getName, "逝者亲友").set(BygSignIn::getNameHit, null);
        // 审核来宾留言
        if (MyEnum.SI_REVIEW_CONTENT.getValue() == type)
            updateWrapper.set(BygSignIn::getContent, null).set(BygSignIn::getContentHit, null);
        if (! update(updateWrapper.in(BygSignIn::getUuId, siUuIds))) return new BaseResDto<>(Status.ERROR);
        return new BaseResDto<>(Status.SUCCESS);
    }

    /**
     * 承办人查看：获取来宾签名列表/祝福内容列表
     * @param amUuId
     * @param type：0-获取来宾签名列表，1-获取来宾祝福内容列表
     * @param pageInfo
     * @param tClass
     * @param <T>
     * @return
     */
    @Override
    public <T> PageDataResDto<List<T>> findAllSignIns(String amUuId, Integer type, PageInfo pageInfo, Class<T> tClass) {
        // 异步任务编排

        // 异步获取当前告别会数据
        CompletableFuture<BygAppointment> future0 = CompletableFuture.supplyAsync(() -> {
            LambdaQueryWrapper<BygAppointment> wrapper = Wrappers
                    .lambdaQuery(BygAppointment.class).eq(BygAppointment::getUuId, amUuId);
            BygAppointment appointment = bygAppointmentService.getOne(wrapper);
            if (appointment == null) throw new RuntimeException(Status.PARAMETERERROR.getMessage());
            return appointment;
        }, threadPoolExecutor);

        // select from byg_sign_in
        // 异步获取签到表中的签到数据集合
        CompletableFuture<IPage<BygSignIn>> future1 = future0.thenApplyAsync(appointment -> {
            QueryWrapper<BygSignIn> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("am_uu_id", appointment.getUuId());
//            if (type == 0) queryWrapper.isNotNull("name").isNull("name_hit");
//            if (type == 1) queryWrapper.isNotNull("content").isNull("content_hit");
            if (type == 0) queryWrapper.isNotNull("name");
            if (type == 1) queryWrapper.isNotNull("content");
            queryWrapper.orderByAsc("in_time");
            return page(new Page<>(pageInfo.getPage(), pageInfo.getSize()), queryWrapper);
        }, threadPoolExecutor);

        // select from byg_sign_in_history
        // 异步获取历史签到表中的签到数据集合
        CompletableFuture<IPage<BygSignInHistory>> future2 = future0.thenApplyAsync(appointment -> {
            QueryWrapper<BygSignInHistory> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("am_uu_id", appointment.getUuId());
//            if (type == 0) queryWrapper.isNotNull("name").isNull("name_hit");
//            if (type == 1) queryWrapper.isNotNull("content").isNull("content_hit");
            if (type == 0) queryWrapper.isNotNull("name");
            if (type == 1) queryWrapper.isNotNull("content");
            queryWrapper.orderByAsc("in_time");
            return bygSignInHistoryService.page(new Page<>(pageInfo.getPage(), pageInfo.getSize()), queryWrapper);
        }, threadPoolExecutor);

        // 因为承办人查看时签到数据是否已由签到表迁移到历史签到表中不确定，所以要做结果比对已确定最终结果
        CompletableFuture<PageDataResDto<List<T>>> future4 = future1.thenCombineAsync(future2, (p1, p2) -> {
            List<BygSignIn> p1Records = p1.getRecords();
            List<BygSignInHistory> p2Records = p2.getRecords();
            List<?> records = p1Records.size() > p2Records.size() ? p1Records : p2Records;
            List<T> tList = records.parallelStream().map(si ->
                    BaseService.copyProperties(si, tClass)).collect(Collectors.toList());
            Long total = p1.getTotal() > p2.getTotal() ? p1.getTotal() : p2.getTotal();
            return new PageDataResDto<List<T>>(Status.SUCCESS).setData(tList)
                    .setPageInfo(pageInfo.setTotal(total));
        });

        try {
            return future4.get();
        } catch (Exception e) {
            if (e.getMessage().contains(Status.PARAMETERERROR.getMessage()))
                return new PageDataResDto<>(Status.PARAMETERERROR);
            return new PageDataResDto<>(Status.ERROR);
        }
    }

}
